import com.sun.org.apache.xpath.internal.objects.XObject;

import java.util.Random;
import java.util.Scanner;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;


/*
* 线程状态            yield
* synchronized      volatile
* wait              notify
* 阻塞队列           BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue
* 定时器             Timer 类的核心方法为 schedule
* */


public class ThreadStateTransfer {
    public static void main1(String[] args) {
        //关注 NEW 、 RUNNABLE 、 TERMINATED 状态的转换
        Thread t = new Thread(() -> {
            for (int i = 0; i < 10000000; i++) {

            }
        }, "李四");
        System.out.println(t.getName() + ":" + t.getState());//getState()   用于获取特定对象状态
        t.start();
        while (t.isAlive()) {
            System.out.println(t.getName() + ":" + t.getState());
        }
        System.out.println(t.getName() + ":" + t.getState());
    }




    public static void main2(String[] args) {
        //关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                /*synchronized  是Java中的一个关键字，用于实现线程同步。
                当一个方法或代码块被  synchronized  修饰时，
                只有一个线程可以进入该方法或代码块，其他线程需要等待。
                这样可以避免多个线程同时访问共享资源而
                导致的数据不一致或竞态条件问题。*/
                synchronized (object) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("hehe");
                }
            }
        }, "h2");
        t2.start();
    }




    public static void main3(String[] args) {
        //1. 不使用 yield 的时候, 张三李四大概五五开
        //2. 使用 yield 时, 张三的数量远远少于李四
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("zhangsan");
                    Thread.yield();
                }
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("lisi");
                    //Thread.yield();
                }
            }
        }, "t2");
        t2.start();
    }




    //原子性   可见性      代码顺序性


    public static void main4(String[] args) throws InterruptedException {
        //解决之前的线程不安全问题
        final Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter.count);
    }

    static class Counter {
        public int count = 0;

        synchronized void increase() {
            count++;
        }
    }


    //synchronized
    /*特性
    * 1)互斥
    *
    * synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到
      同一个对象 synchronized 就会阻塞等待
    * 进入 synchronized 修饰的代码块, 相当于 加锁
      退出 synchronized 修饰的代码块, 相当于 解锁
      *
    * 2)刷新内存
    * 1. 获得互斥锁
    2. 从主内存拷贝变量的最新副本到工作的内存
    3. 执行代码
    4. 将更改后的共享变量的值刷新到主内存
    5. 释放互斥锁
    *
    * 3) 可重入
    *synchronized 同步块对同一条线程来说是可重入的，不会出现自己把自己锁死的问题
    *
    *
    *1)直接修饰普通方法: 锁的 SynchronizedDemo 对象
    *2) 修饰静态方法: 锁的 SynchronizedDemo 类的对象
    *3) 修饰代码块: 明确指定锁哪个对象
    *
    *
    *
    * */

    /*
     *  volatile 关键字
     * volatile 能保证内存可见性
     *volatile 不保证原子性
     * */

    static class Counter1 {
        public volatile static int flag = 0;
    }
    public static void main5(String[] args) {
        Counter1 counter1 = new Counter1();
        Thread t1 = new Thread(() -> {
            while (counter1.flag == 0) {

            }
            System.out.println("循环结束！");
        });
        Thread t2 = new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("输入一个整数");
            Counter1.flag = scanner.nextInt();
        });
        t1.start();
        t2.start();

    }



    /*
    * wait 和  notify
    * wait
    * 使当前执行代码的线程进行等待. (把线程放到等待队列中)
    释放当前的锁
    满足一定条件时被唤醒, 重新尝试获取这个锁
    * wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常
    *wait 结束等待的条件:
    其他线程调用该对象的 notify 方法.
    wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).
    其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常
    *
    *
    * notify
    * notify 方法是唤醒等待的线程.
方法notify()也要在同步方法或同步块中调用，该方法是用来通知那些可能等待该对象的对象锁的
其它线程，对其发出通知notify，并使它们重新获取该对象的对象锁。
如果有多个线程等待，则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 "先来后到")
在notify()方法后，当前线程不会马上释放该对象锁，要等到执行notify()方法的线程将程序执行
完，也就是退出同步代码块之后才会释放对象锁。
    *
    *
    * 代码示例: 使用notify()方法唤醒线程
创建 WaitTask 类, 对应一个线程, run 内部循环调用 wait.
创建 NotifyTask 类, 对应另一个线程, 在 run 内部调用一次 notify
注意, WaitTask 和 NotifyTask 内部持有同一个 Object locker. WaitTask 和 NotifyTask 要想配合
就需要搭配同一个 Object.
    *
    *
    *
    * */
    public static class WaitTask implements Runnable{
        private Object locker;
        public WaitTask(Object locker){
            this.locker=locker;
        }

        @Override
        public void run() {
            synchronized (locker){
                while(true){
                    try {
                        System.out.println("wait开始");
                        locker.wait();
                        System.out.println("wait结束");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
    public static class NotifyTask implements Runnable{
        private Object locker;
        public NotifyTask(Object locker){
            this.locker=locker;
        }

        @Override
        public void run() {
            synchronized (locker){

                    System.out.println("notify开始");
                    locker.notifyAll();
                    System.out.println("notify结束");

            }
        }
    }
    public static void main6(String[] args) throws InterruptedException {
        Object locker = new Object();
        Thread t1 = new Thread(new WaitTask(locker));
        Thread t2 = new Thread(new WaitTask(locker));
        Thread t3 = new Thread(new WaitTask(locker));
        Thread t4 = new Thread(new WaitTask(locker));
        Thread t5=new Thread(new NotifyTask(locker));
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        Thread.sleep(1000);
        t5.start();
    }
/*
虽然是同时唤醒 3 个线程, 但是这 3 个线程需要竞争锁. 所以并不是同时执行, 而仍然是有先有后的
执行.*/

    /*
    * 7.4 wait 和 sleep 的对比（面试题）
其实理论上 wait 和 sleep 完全是没有可比性的，因为一个是用于线程之间的通信的，一个是让线程阻
塞一段时间，
唯一的相同点就是都可以让线程放弃执行一段时间.
当然为了面试的目的，我们还是总结下：
1. wait 需要搭配 synchronized 使用. sleep 不需要.
2. wait 是 Object 的方法 sleep 是 Thread 的静态方法
* */




    //阻塞队列
    /*
    * 标准库中的阻塞队列
在 Java 标准库中内置了阻塞队列. 如果我们需要在一些程序中使用阻塞队列, 直接使用标准库中的即可.
BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue.
put 方法用于阻塞式的入队列, take 用于阻塞式的出队列.
BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性
    * */


    /*
    * 生产者消费者模型
    * */
    /*7*/

    private int[] items =new int[1000];
    private volatile int size=0;
    private int head =0;
    private int tail=0;
    public void put(int value) throws InterruptedException {
        synchronized (this){
            while(size==items.length){
                wait();
            }
            items[tail]=value;
            tail = (tail+1)%items.length;
            size++;
            notifyAll();
        }
    }

    public int take() throws InterruptedException {
        int ret=0;
        synchronized (this){
            while(size==0){
                wait();
            }
            ret=items[head];
            head = (head+1)% items.length;
            size--;
            notifyAll();
        }
        return ret;
    }
    public synchronized int size(){
        return size;
    }

    public static void main7(String[] args) throws InterruptedException {
        BlockingQueue<Integer> blockingQueue =new LinkedBlockingQueue<Integer>();

        Thread customer = new Thread(()->{
            while(true){
                try {
                    int value= blockingQueue.take();
                    System.out.println("消费元素："+value);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"消费者");
        customer.start();
        Thread producer =new Thread(()->{
            Random random = new Random();
            while(true){
                try {
                    int num=random.nextInt(1000);
                    System.out.println("生产元素"+num);
                    blockingQueue.put(num);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"生产者");
        producer.start();
        customer.join();
        producer.join();
    }



/*
*
* 标准库中的定时器
标准库中提供了一个 Timer 类. Timer 类的核心方法为 schedule .
schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后
执行 (单位为毫秒).*/

/*
* 一个带优先级的阻塞队列
为啥要带优先级呢?
因为阻塞队列中的任务都有各自的执行时刻 (delay). 最先执行的任务一定是 delay 最小的. 使用带
优先级的队列就可以高效的把这个 delay 最小的任务找出来.
队列中的每个元素是一个 Task 对象.
Task 中带有一个时间属性, 队首元素就是即将
同时有一个 worker 线程一直扫描队首元素, 看队首元素是否需要执行
* */


}
